package org.as3search.criteria.impl {
	/*
	 * as3search - http://code.google.com/p/as3search
	 * Copyright (c) 2009-2010 by respective authors. All rights reserved.
	 *
	 * This library is free software; you can redistribute it and/or modify it under the
	 * terms of the GNU Lesser General Public License as published by the Free Software
	 * Foundation; either version 2.1 of the License, or (at your option) any later
	 * version.
	 *
	 * This library is distributed in the hope that it will be useful, but WITHOUT ANY
	 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
	 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
	 *
	 * You should have received a copy of the GNU Lesser General Public License along
	 * with this library; if not, write to the Free Software Foundation, Inc.,
	 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
	 */
	
	import flash.errors.IllegalOperationError;
	
	import org.as3search.Search;
	import org.as3search.config.SearchConfiguration;
	import org.as3search.config.SearchConfigurationAware;
	import org.as3search.criteria.Criteria;
	import org.as3search.criteria.Criterion;
	import org.as3search.criteria.SubCriteria;
	import org.as3search.properties.PropertyResolverAware;
	import org.as3search.providers.SearchProvider;
	
	/**
	 * <code>SubCriteriaImpl</code> is the default <code>SubCriteria</code>
	 * implmentation for as3search. At this time, it provides subcollection
	 * searching, but does not provide a facility for fetching items in 
	 * said subcollection yet. Future versions of the library will address
	 * these shortcomings.
	 * 
	 * @see org.as3search.criteria.SubCriteria
	 * @see org.as3search.criteria.Criteria
	 */
	public class SubCriteriaImpl implements SubCriteria {
		
		private var _propertyName:String;
		
		private var _criteriaCollection:Array = [];
		
		private var _configuration:SearchConfiguration;
		
		private var _parent:Criteria;
		/**
		 * Creates a new <code>SubCriteriaImpl</code> instance.
		 * @param	propertyName	The property name of the subcollection.
		 * @param	criterion		The <code>Criterion</code> instance to apply to items in the
		 * 		<code>propertyName</code> collection.
		 * @param	configuration	The <code>SearchConfiguration</code> to use in resolving properties
		 * 		and iterating collections.
		 * @param	parent			The parent <code>Criteria</code> instance to which this
		 * 		<code>SubCriteria</code> belongs.
		 * 
		 * @see org.as3search.criteria.Criteria
		 * @see org.as3search.config.SearchConfiguration
		 * @see org.as3search.criteria.Criterion
		 */
		public function SubCriteriaImpl(propertyName:String, criterion:Criterion,
				configuration:SearchConfiguration, parent:Criteria) {
			if (propertyName == null || !(new RegExp("^\\w[\\w\\.]*\\w$").test(propertyName)))
				throw new ArgumentError("Property name cannot be null/invalid!");
			
			_propertyName = propertyName;
			_parent = parent;
			_configuration = (configuration == null ? Search.configuration : configuration);
			
			if (criterion != null)
				add(criterion);
		}
		/**
		 * @inheritDoc
		 */
		public function evaluate(value:Object):Boolean {
			var provider:SearchProvider = configuration.getSearchProvider(
				configuration.propertyResolver.resolveProperty(propertyName, value));
			while (provider.hasNext()) {
				var target:Object = provider.next();
				var matches:int = 0;
				for (var i:uint = 0; i < criteriaCollection.length; i++) {
					var criterion:Criterion = Criterion(criteriaCollection[i]);
					if (criterion.evaluate(target))
						matches++;
					else 
						break;
				}
				if (matches == criteriaCollection.length)
					return true;
			}
			
			return false;
		}
		/**
		 * @inheritDoc
		 */
		public function add(value:Criterion):Criteria {
			if (value == null)
				throw new ArgumentError("values passed to add() must not be null!");
			
			if (criteriaCollection.indexOf(value) != -1)
				throw new ArgumentError("You cannot add the same criterion more than once!");
			
			if (value is SearchConfigurationAware)
				SearchConfigurationAware(value).configuration = configuration;
			
			if (value is PropertyResolverAware)
				PropertyResolverAware(value).propertyResolver = configuration.propertyResolver;
			
			criteriaCollection.push(value);			
			return this;
		}
		/**
		 * This method is not available for <code>SubCriteriaImpl</code>. In the current version
		 * of as3search, it is not possible to <code>createCriteria()</code> on a <code>SubCriteria</code>.
		 * If you need to create subcriteria, do it from the master <code>Criteria</code> 
		 * instance.
		 * @param	propertyName	The property name.
		 * @param	criterion		The <code>Criterion</code> instance.
		 * @return	Returns this for method chaining.
		 * @throws flash.errors.IllegalOperationError	<code>SubCriteria</code> cannot create
		 * 		other <code>SubCriteria</code>! Please create subcriteria from the master 
		 * 		<code>Criteria</code> instance.
		 */
		public function createCriteria(propertyName:String, criterion:Criterion):Criteria {
			throw new IllegalOperationError("SubCriteria cannot create other SubCriteria! " +
				"Please create subcriteria from the master Criteria instance.");
			return this;
		}
		/**
		 * This method is not available for <code>SubCriteriaImpl</code>. In the current version
		 * of as3search, it is not posible to <code>uniqueResult()</code> on a subcollection.
		 * @return Returns null.
		 * @throws flash.errors.IllegalOperationError <code>SubCriteria</code> cannot return a 
		 * 		unique result... yet.
		 */
		public function uniqueResult():Object {
			throw new IllegalOperationError("SubCriteria cannot return a unique result... yet.");
			return null;
		}
		/**
		 * This method is not available for <code>SubCriteriaImpl</code>. In the current version
		 * of as3search, it is not possible to <code>list()</code> on a subcollection.
		 * @return Returns null
		 * @throws flash.errors.IllegalOperationError <code>SubCriteria</code> cannot return their own lists... yet.
		 */
		public function list():Array {
			throw new IllegalOperationError("SubCriteria cannot return their own list... yet.");
			return null;
		}
		/**
		 * This method is not available for <code>SubCriteriaImpl</code>. In the current version
		 * of as3search, it is not possible to <code>test()</code> on a subcollection.
		 * @return Returns false;
		 * @throws flash.errors.IllegalOperationError <code>SubCriteria</code> cannot execute tests... yet.
		 */
		public function test():Boolean {
			throw new IllegalOperationError("SubCriteria cannot execute test... yet.");
			return false;
		}
		/**
		 * @inheritDoc
		 */
		public function get propertyName():String { return _propertyName; }
		/**
		 * @inheritDoc
		 */
		public function get parent():Criteria { return _parent; }
		/**
		 * @inheritDoc
		 */
		public function get configuration():SearchConfiguration { return _configuration; }
		/**
		 * The <code>Array</code> used to store all <code>Criterion</code> instances used to
		 * evaluate each item in the subcollection.
		 */
		public function get criteriaCollection():Array { return _criteriaCollection; }
		public function set criteriaCollection(value:Array):void {
			if (value == null)
				value = [];
			_criteriaCollection = value;
		}
	}
}